using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using ProjectEuler.Core.Helpers;

namespace ProjectEuler.Core
{
    public class Problem58 : IProjectEulerProblem
    {
        protected int Get10PercentSpiralLength()
        {
            var starter = 2;
            var spiralLength = 1;
            var primeDiagonals = new List<int>();
            var numDiagonals = 1;
            while(true)
            {
                spiralLength += 2;

                var diagonals = GetDiagonals(starter, spiralLength);
                primeDiagonals.AddRange(diagonals.Where(MathHelper.IsPrime));
                numDiagonals += 4;

                var ratio = primeDiagonals.Count*1.0/numDiagonals*1.0;
                if(ratio < 0.10)
                {
                    return spiralLength;
                }
                starter = diagonals.Max()+1;
            }
        }

        protected IList<int> GetDiagonals(int start, int spiralLength)
        {
            if(spiralLength ==1)
            {
                return new List<int> {1};
            }
            var nums = Enumerable.Range(start, (spiralLength - 1)*4);
            var diagonals = new List<int>();

            diagonals.Add(nums.Skip(spiralLength-2).First());
            diagonals.Add(nums.Skip(spiralLength-2+spiralLength-1).First());
            diagonals.Add(nums.Skip(spiralLength - 2 + spiralLength - 1 + spiralLength - 1).First());
            diagonals.Add(nums.Skip(spiralLength - 2 + spiralLength - 1 + spiralLength - 1 + spiralLength - 1).First());

            return diagonals;
        }

        public int Number
        {
            get { return 58; }
        }

        public string Description
        {
            get { return "What is the side length of the square spiral for which the ratio of primes along both diagonals first falls below 10%?"; }
        }

        public string Answer
        {
            get { return Get10PercentSpiralLength().ToString(); }
        }
    }

    [TestFixture]
    public class Problem58Tests : Problem58
    {
        [Test]
        public void A_3x3_grid_should_have_the_correct_diagonals()
        {
            var diagonals3x3 = GetDiagonals(2, 3);
            Assert.AreEqual(4, diagonals3x3.Count);
            Assert.IsTrue(diagonals3x3.Contains(3));
            Assert.IsTrue(diagonals3x3.Contains(5));
            Assert.IsTrue(diagonals3x3.Contains(7));
            Assert.IsTrue(diagonals3x3.Contains(9));
        }

        [Test]
        public void A_5x5_grid_should_have_the_correct_diagonals()
        {
            var diagonals5x5 = GetDiagonals(10, 5);
            Assert.AreEqual(4, diagonals5x5.Count);
            Assert.IsTrue(diagonals5x5.Contains(13));
            Assert.IsTrue(diagonals5x5.Contains(17));
            Assert.IsTrue(diagonals5x5.Contains(21));
            Assert.IsTrue(diagonals5x5.Contains(25));
        }

        [Test]
        public void A_7x7_grid_should_have_3_out_of_4_prime_diagonals()
        {
            var diagonals = GetDiagonals(26, 7);
            var primes = diagonals.Where(d => MathHelper.IsPrime(d));

            Assert.AreEqual(4, diagonals.Count);
            Assert.AreEqual(3, primes.Count());
        }
    }
}